package cn.xinfei.xdecision.manager.controller.engine;

import cn.xinfei.xdecision.common.model.common.ResponseEntityBuilder;
import cn.xinfei.xdecision.common.model.common.ResponseEntityDto;
import cn.xinfei.xdecision.common.model.enginex.po.EngineVersion;
import cn.xinfei.xdecision.common.model.enginex.vo.EngineVersionReqVo;
import cn.xinfei.xdecision.common.model.enginex.vo.EngineVersionRespVo;
import cn.xinfei.xdecision.common.model.login.SSOUser;
import cn.xinfei.xdecision.common.model.login.SessionManager;
import cn.xinfei.xdecision.common.service.enginex.EngineVersionService;
import cn.xinfei.xdecision.common.utils.constant.OperateNameConst;
import cn.xinfei.xdecision.common.utils.constant.OperateTypeEnum;
import cn.xinfei.xdecision.common.utils.constant.RedisConstant;
import cn.xinfei.xdecision.common.utils.enums.enginex.EngineVersionStatusEnum;
import cn.xinfei.xdecision.common.utils.exception.ApiException;
import cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum;
import cn.xinfei.xdecision.manager.web.aop.ArchivesLog;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.text.MessageFormat;
import java.util.List;
import java.util.Objects;

import static cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum.ENGINE_EDITING_OTHER;
import static cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum.ENGINE_MUST_BE_EDIT_DOWN;

@Api(tags = "引擎管理-决策流-版本管理")
@RestController("EngineVersionControllerV3")
@RequestMapping("/v3/engineVersion")
@Slf4j
public class EngineVersionController {

    @Autowired
    private EngineVersionService engineVersionService;

    @Resource
    private RedissonClient redissonClient;

    @GetMapping(value = "/versionList/{engineCode}")
    @ApiOperation(value = "根据engineCode获取引擎下所有版本",
            notes = "根据engineCode获取引擎下所有版本")
    @ResponseBody
    public ResponseEntityDto<List<EngineVersionRespVo>> getEngineVersionList(@PathVariable String engineCode) {
        // 查出引擎下所有版本
        List<EngineVersionRespVo> engineVersions = engineVersionService.getEngineVersionListByEngineCode(engineCode);
        return ResponseEntityBuilder.buildNormalResponse(engineVersions);
    }

    @GetMapping(value = "/runningVersion/{engineCode}")
    @ApiOperation(value = "根据engineCode获取引擎正在运行中的版本",
            notes = "根据engineCode获取引擎正在运行中的版本")
    @ResponseBody
    public ResponseEntityDto<EngineVersion> getRunningVersion(@PathVariable String engineCode) {
        EngineVersion engineVersion = engineVersionService.getVersionByStatus(engineCode, EngineVersionStatusEnum.ENABLE.getStatus());
        return ResponseEntityBuilder.buildNormalResponse(engineVersion);
    }


    @PostMapping(value = "/copyAndCreate")
    @ApiOperation(value = "复制当前版本并创建新版本",
            notes = "复制当前版本并创建新版本")
    @ArchivesLog(type = OperateTypeEnum.CREATE, name = OperateNameConst.DecisionFlowOpt.SAVE_VERSION)
    @ResponseBody
    public ResponseEntityDto<EngineVersion> copyAndCreateVersion(@RequestBody @Valid EngineVersionReqVo engineVersionReqVo) {
        //加锁
        String key = MessageFormat.format(RedisConstant.RISK_ENGINE_BASIC_INFO_KEY,
                RedisConstant.DECISION_FLOW,
                RedisConstant.DECISION_FLOW_COPY,
                engineVersionReqVo.getEngineCode());
        RLock lock = redissonClient.getLock(key);
        if (!lock.tryLock()) {
            return ResponseEntityBuilder.buildErrorResponse(ErrorCodeEnum.ENGINE_NEW_STATE_VERSION_EXISTS);
        }
        try {
            return ResponseEntityBuilder.buildNormalResponse(engineVersionService.copyAndCreateVersion(engineVersionReqVo));
        } catch (Exception e) {
            log.error("复制当前版本并创建新版本异常", e);
            throw e;
        } finally {
            lock.unlock();
        }
    }


    @PostMapping(value = "/clear")
    @ApiOperation(value = "清空画布",
            notes = "根据versionId清空，仅能清空新建状态的版本")
    @ResponseBody
    @ArchivesLog(type = OperateTypeEnum.DELETE, name = OperateNameConst.DecisionFlowOpt.CLEAR_NODE)
    public ResponseEntityDto<Object> clear(@RequestBody @Valid EngineVersionReqVo engineVersionReqVo) {
        return ResponseEntityBuilder.buildNormalResponse(engineVersionService.clear(engineVersionReqVo));

    }

    @PostMapping(value = "/deploy")
    @ApiOperation(value = "部署引擎",
            notes = "部署引擎")
    @ResponseBody
    @ArchivesLog(type = OperateTypeEnum.UPDATE, name = OperateNameConst.DecisionFlowOpt.ENGINE_DEPLOY)
    public ResponseEntityDto<Object> deployEngineVersion(@RequestBody @Valid EngineVersionReqVo engineVersionReqVo) {
        Long loginUserId = SessionManager.getLoginUserId();
        //如果是当前人点击部署或删除，则提示报错
        checkEditLock(engineVersionReqVo.getEngineCode(), engineVersionReqVo.getVersionNo(), loginUserId);

        //加锁
        String key = MessageFormat.format(RedisConstant.RISK_ENGINE_BASIC_INFO_KEY,
                RedisConstant.DECISION_FLOW,
                RedisConstant.DECISION_FLOW_EDIT,
                engineVersionReqVo.getEngineCode());
        RLock lock = redissonClient.getLock(key);
        if (!lock.tryLock()) {
            return ResponseEntityBuilder.buildErrorResponse(ErrorCodeEnum.ENGINE_CONCURRENT_OPERATE_ERROR);
        }
        try {
            return ResponseEntityBuilder.buildNormalResponse(engineVersionService.deployEngine(loginUserId, engineVersionReqVo));
        } catch (Exception e) {
            log.error("部署引擎异常", e);
            throw e;
        } finally {
            lock.unlock();
        }
    }

    @PostMapping(value = "/undeploy")
    @ApiOperation(value = "下线引擎", notes = "下线引擎")
    @ResponseBody
    @ArchivesLog(type = OperateTypeEnum.UPDATE, name = OperateNameConst.DecisionFlowOpt.ENGINE_UNDEPLOY)
    public ResponseEntityDto<Object> undeploy(@RequestBody @Valid EngineVersionReqVo engineVersionReqVo) {

        return ResponseEntityBuilder.buildNormalResponse(engineVersionService.undeployEngine(engineVersionReqVo));

    }

    @DeleteMapping(value = "/delete")
    @ApiOperation(value = "删除版本", notes = "删除版本")
    @ResponseBody
    @ArchivesLog(type = OperateTypeEnum.DELETE, name = OperateNameConst.DecisionFlowOpt.DELETE_VERSION)
    public ResponseEntityDto<Object> deleteVersion(@RequestBody @Valid EngineVersionReqVo engineVersionReqVo) {
        //如果是当前人点击部署或删除，则提示报错
        checkEditLock(engineVersionReqVo.getEngineCode(), engineVersionReqVo.getVersionNo(), SessionManager.getLoginUserId());
        return ResponseEntityBuilder.buildNormalResponse(engineVersionService.deleteVersion(engineVersionReqVo));

    }

    @PostMapping(value = "/inoutVars")
    @ApiOperation(value = "出入参归总",
            notes = "出入参归总")
    @ResponseBody
    public ResponseEntityDto<Object> inoutVars(@RequestBody @Valid EngineVersionReqVo engineVersionReqVo) {

        Long loginUserId = SessionManager.getLoginUserId();
        return ResponseEntityBuilder.buildNormalResponse(engineVersionService.getInOutVarList(engineVersionReqVo.getEngineCode(), engineVersionReqVo.getVersionNo(), loginUserId));
    }

    private void checkEditLock(String engineCode, Long versionNo, Long loginUserId) {
        String key = MessageFormat.format(RedisConstant.RISK_ENGINE_BASIC_INFO_KEY,
                RedisConstant.DECISION_FLOW,
                RedisConstant.DECISION_FLOW_EDIT,
                engineCode + "_" + versionNo);
        RLock lock = redissonClient.getLock(key);
        if (lock.isLocked()) {
            RBucket<SSOUser> bucket = redissonClient.getBucket(key);
            SSOUser user = bucket.get();
            if (Objects.equals(user.getId(), loginUserId)) {
                throw new ApiException(ENGINE_MUST_BE_EDIT_DOWN);
            }
            throw new ApiException(ENGINE_EDITING_OTHER.getCode(),
                    MessageFormat.format(ENGINE_EDITING_OTHER.getMessage(), user.getName()));
        }
    }

}
